一直很想把Patten matching、Case Class、Extrator、Sealed的相關概念寫一下,就再來個番外吧。今天沒有Spark,但是有很重要的Scala概念!!
[Snippet.47] Patten match Basic
先來個暖身操,Scala中沒有switch可以用,但是他有pattern match
,寫法如下:
//1. C or Java-style
var sign = 0;
val ch: Char = '+'
ch match { ①
case '+' => sign = 1 ②
case '-' => sign = -1 ③
case _ => sign = 0 ④
}
①對ch
變數作patten match操作,有點類似其他語言的switch(ch)
②看到case
有沒有有點親切,但是Scala中的case可厲害多了,啥都能拿來比勒,繼續看下去吧
③判斷並對sign
賦值
④_
就是以前寫函式常用的placeholder
啦,在patten match中的角色類似default或是Others。
pattern match跟C或Java一樣都會從一個case
開始往下比對,但是比到就結束跳出了
,不會有所謂的fall-through
現象,所以也不用break
定義退出點。
而pattern match不只是一段statement,他就像一般函數一樣,可以是表示式(expression)。因此上面sign賦值的方式可以寫成這樣:
sign = ch match {
case '+' => 1
case '-' => 1
case _ => 0
}
[Snippet.48] Patten match with Guards
而patten match在case判斷時,還能有所謂Guards的概念:
var digit: Int = 20;
ch match {
case '+' => 1
case '-' => 1
case _ if Character.isDigit(ch) => digit = Character.digit(ch, 10) ①
case _ => 0
}
①可以看到一樣是接_
,但還能限定條件,Guards可是以任何返回Boolean的函式
。另外別忘了順序,後面兩個判斷的順序別放反了。
[Snippet.49] Patten match with variable
就像switch,Pattern的比較對象當然也能是variable:
//3. variable in pattern
var anotherCh: Char = '3'
var char: Char = 'a'
anotherCh match {
case '+' => 1
case '-' => 1
case ch => digit = Character.digit(ch, 10) ①
case ch if Character.isLetter(ch) => char = ch;
}
①ch
除了拿來比較,還能用在match後的body
中,反正就是個變數阿XD
[Snippet.50] Patten match in a Function
patten match當然也能搭配函式使用囉:
// Used in Int function
def matchTest(x: Int): String = x match {
case 1 => "one"
case 2 => "two"
case _ => "many"
}
println(matchTest(3)) ①
①呼叫函數並印出回傳值
[Snippet.51] Type match
patten match還能匹配型別!???
def matchTest2(x: Any): Any = x match {
case 1 => "one"
case "two" => 2
case y: Int => "scala.Int" ①
}
println(matchTest2("two"))
println(matchTest2(1))
println(matchTest2(2))
//Output:
//2
//one
//scala.Int
假設前面的都沒過,比到①時,若值為整數型別(y:Int)
,則輸出字串"scala.Int"。
[Snippet.52] Type match-2
一堆判斷型別大亂鬥:
var obj: Any = "20"
obj match {
case x: Int => x
case y: String => Integer.parseInt(y)
case _: BigInt => Int.MaxValue
case _ => 0
}
所以`InstanceOf`可以退場了,用pattern match取代吧!
[Snippet.53] pattern match with | operator
還有這種比較酷的寫法:
def foo(msg: Any) = msg match {
case (_, 1) | (1, _) => "Tuple and has one"
case _=> "Others"
}
foo((3, 1))
foo((1, 5))
foo(List())
[Snippet.54] pattern match with arrays, List, and Pair(Tuple2)
另外pattern match也很常用在集合物件
上,先看看Array:
//Array
def matchArr(arr: Any): Any =
arr match {
case Array(0) => "0" ①
case Array(0, z) => "0..." ②
case Array(x, y) => x + " " + y ③
case _ => "Something else"
}
matchArr(Array(0))
matchArr(Array(3, 4))
matchArr(Array(0, 4))
matchArr(Array(3, 4, 5))
//Output:
//0
//3 4
//0...
//Something else
①match只有一個元素(0)的情況
②match兩個元素的Array,其中第一個還是0的情況
③match任意兩個元素的Array,所以②要放在③之前
List:
def matchList(list: Any): Any =
list match {
case Nil => "empty list" ①
case 0 :: tail => "0..." ②
case x :: y :: Nil => "two elements only" ③
case x :: y => x + " " + y ④
case _ => "Something else"
}
matchList(List())
matchList(List(3, 5))
matchList(List(3, 5, 7))
matchList(List(0, 5, 7, 9))
//Output:
//empty list
//two elements only
//3 List(5, 7)
//0...
還記得::
這個符號的意思嗎?忘記快去翻前面XDD
基本上List很常在遞迴拆解List的時候用用上head::tail_list這種結構,然後再次把tail_list丟入func之類的。
①match空List的情況
②match第一個元素是0的情況
③match只有兩個元素的List!
④match除了Nil List之外的任意情況。
想一下matchList(List(3))
答案是啥呢?就是3 List()
。
Pair:
def matchTuple(tuple: Any): Any =
tuple match {
case (0, _) => "0.."
case (_, 0) => "..0"
case _ => "Neither is 0"
}
matchTuple((0, 1))
matchTuple((2, 0))
matchTuple((1, 2))
Pair的輸出就不多解釋啦~